use std::collections::hash_map::Entry;
use std::collections::HashMap;

use crate::http_headers::{HttpHeader, HttpHeaderValue};

#[derive(Clone, Debug)]
pub enum ResponseBody {
    // 字符串数据
    StrBody(String),
    // 直接数据
    BytesBody(Vec<u8>)
}

impl ResponseBody {
    pub fn to_bytes_vec(&self) -> Vec<u8> {
        let mut response_buffer: Vec<u8> =  Vec::new();
        match self {
            ResponseBody::StrBody(str_body) => {
                response_buffer.extend(str_body.trim().as_bytes());
            },
            ResponseBody::BytesBody(bytes_body) => {
                response_buffer.extend(bytes_body);
            }
        };
        response_buffer
    }
}

impl Into<Vec<u8>> for ResponseBody {
    fn into(self) -> Vec<u8> {
        self.to_bytes_vec()
    }
}

#[derive(Clone, Debug)]
pub struct HttpResponse<'a> {
    // 响应状态
    pub status_line: ResponseStatusLine<'a>,
    // 响应头
    pub headers: HashMap<&'a str, HttpHeaderValue<'a>>,
    // 响应体
    pub body: Option<ResponseBody>
}

#[derive(Clone, Debug)]
pub struct ResponseStatusLine<'a> {
    pub http_version: &'a str,
    pub status_code: u32,
    pub reason: &'a str,
}

impl<'a> ResponseStatusLine<'a> {

    #[allow(dead_code)]
    pub const fn new(http_version: &'a str, status_code: u32, reason: &'a str) -> ResponseStatusLine<'a> {
        ResponseStatusLine {
            http_version,
            status_code,
            reason,
        }
    }

    pub const fn success(reason: &'a str) -> ResponseStatusLine<'a> {
        ResponseStatusLine {
            http_version: "HTTP/1.1",
            status_code: 200,
            reason,
        }
    }

    pub const fn error(status_code: u32, reason: &'a str) -> ResponseStatusLine<'a> {
        ResponseStatusLine {
            http_version: "HTTP/1.1",
            status_code,
            reason,
        }
    }
}

impl<'a> HttpResponse<'a> {

    pub fn new(status_line: ResponseStatusLine<'a>) -> HttpResponse<'a> {
        HttpResponse {
            status_line,
            headers: Default::default(),
            body: None,
        }
    }

    pub fn add_header(&mut self, http_header: HttpHeader<'a>)  {
        if !http_header.name.is_empty() {
            self.headers.insert(http_header.name.trim(), http_header.value);
        }
    }

    #[allow(dead_code)]
    pub fn get_header(&mut self, key: &'a str) -> String {
        match self.headers.entry(key) {
            Entry::Occupied(e) => e.get().to_string(),
            Entry::Vacant(_) => "".to_string()
        }
    }
}

impl<'a> Into<String> for ResponseStatusLine<'a> {
    fn into(self) -> String {
        format!("{} {} {}",
            if self.http_version.is_empty() { "HTTP/1.1" } else { self.http_version.trim() },
            self.status_code,
            if self.reason.is_empty() { "Unknown Error" } else { self.reason.trim() }
        )
    }
}

impl<'a> Into<Vec<u8>> for HttpResponse<'a> {
    fn into(self) -> Vec<u8> {
        let response_without_body = format!("{}\r\n{}\r\n",
                Into::<String>::into(self.status_line),
                self.headers.iter().map(|x| format!("{}: {}", x.0, x.1.to_string())).collect::<Vec<String>>().join("\r\n") + "\r\n",
        );
        let mut response_buffer: Vec<u8> =  Vec::new();
        response_buffer.extend(response_without_body.as_bytes());
        if let Some(data) = self.body {
            response_buffer.extend(Into::<Vec<u8>>::into(data));
        };
        response_buffer
    }
}